#include "stdafx.h"
#include "Scheduler.h"

#include "Registry.h"
#include "Task.h"
#include "FrameTaskTrigger.h"
#include "TimeTaskTrigger.h"
#include "EveryFrameTaskTrigger.h"

#ifdef WIN32
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif //_DEBUG
#endif //WIND32

namespace serika
{
	namespace ut
	{
		Scheduler::Scheduler()
			: mIdSeed(0)
		{
			Registry<Scheduler>::Set(this);
		}

		Scheduler::~Scheduler()
		{
			for (InfoMap::iterator iter = mInfoMap.begin(); iter != mInfoMap.end(); ++iter)
			{
				InfoList& infoList = iter->second;
				for (InfoList::iterator listIter = infoList.begin(); listIter != infoList.end(); ++listIter)
				{
					TaskInfo* info = listIter->second;
					this->DeleteInfo(info);
				}
				infoList.clear();
			}
			mInfoMap.clear();

			Registry<Scheduler>::Set(0);
		}

		Scheduler* Scheduler::Instance()
		{
			return Registry<Scheduler>::Get();
		}

		void Scheduler::ExecuteTask(const Clock& clock)
		{
			for (InfoMap::iterator iter = mInfoMap.begin(); iter != mInfoMap.end(); ++iter)
			{
				InfoList& infoList = iter->second;
				for (InfoList::iterator listIter = infoList.begin(); listIter != infoList.end(); ++listIter)
				{
					TaskInfo* info = listIter->second;
					this->Execute(info, listIter->first, clock);
				}
			}
		}

		void Scheduler::Execute(TaskInfo* info, int id, const Clock& clock)
		{
			if (info->deleted)
			{
				return;
			}

			if (!info->trigger->CanExecute(clock))
			{
				return;
			}

			info->task->Execute(id);

			bool terminate = info->trigger->CanTerminate(clock);
			if (terminate)
			{
				info->deleted = true;
			}
		}

		int Scheduler::ScheduleEveryFrameTask(Task* task, TaskPriority::Enum priority /* = TaskPriority::Medium */)
		{
			TaskInfo* info = this->NewInfo(task, new EveryFrameTaskTrigger());

			InfoList& infoList = this->GetInfoList(priority);
			int taskId = this->GetId();
			infoList.insert(std::make_pair(taskId, info));

			return taskId;
		}

		int Scheduler::ScheduleRepeatedTimeTask(Task* task, size_t loopInterval, TaskPriority::Enum priority /* = TaskPriority::Medium */)
		{
			return this->ScheduleTask<TimeTaskTrigger>(task, 0, 0, loopInterval, priority);
		}

		int Scheduler::ScheduleOnceTimeTask(Task* task, size_t start, TaskPriority::Enum priority /* = TaskPriority::Medium */)
		{
			return this->ScheduleTask<TimeTaskTrigger>(task, start, start, 0, priority);
		}

		int Scheduler::ScheduleOnceFrameTask(Task* task, size_t start, TaskPriority::Enum priority /* = TaskPriority::Medium */)
		{
			return this->ScheduleTask<FrameTaskTrigger>(task, start, start, 0, priority);
		}

		int Scheduler::ScheduleTimeTask(Task* task,
			size_t start, size_t end, size_t loopInterval,
			TaskPriority::Enum priority /* = TaskPriority::Medium */)
		{
			return this->ScheduleTask<TimeTaskTrigger>(task, start, end, loopInterval, priority);
		}

		int Scheduler::ScheduleFrameTask(Task* task,
			size_t start, size_t end, size_t loopInterval,
			TaskPriority::Enum priority /* = TaskPriority::Medium */)
		{
			return this->ScheduleTask<FrameTaskTrigger>(task, start, end, loopInterval, priority);
		}

		template <class Trigger>
		inline int Scheduler::ScheduleTask(Task* task,
			size_t start, size_t end, size_t loopInterval,
			TaskPriority::Enum priority)
		{
			TaskTrigger* trigger = new Trigger(start, end, loopInterval);
			TaskInfo* info = this->NewInfo(task, trigger);

			InfoList& infoList = this->GetInfoList(priority);
			int taskId = this->GetId();
			infoList.insert(std::make_pair(taskId, info));

			return taskId;
		}

		void Scheduler::TerminateTask(Task* task)
		{
			for (InfoMap::iterator iter = mInfoMap.begin(); iter != mInfoMap.end(); ++iter)
			{
				InfoList& infoList = iter->second;
				for (InfoList::iterator infoIter = infoList.begin(); infoIter != infoList.end(); ++infoIter)
				{
					TaskInfo* info = infoIter->second;
					if (info->task == task)
					{
						info->deleted = true;
					}
				}
			}
		}

		void Scheduler::TerminateTask(int id)
		{
			for (InfoMap::iterator iter = mInfoMap.begin(); iter != mInfoMap.end(); ++iter)
			{
				InfoList& infoList = iter->second;
				for (InfoList::iterator infoIter = infoList.begin(); infoIter != infoList.end(); ++infoIter)
				{
					if (infoIter->first == id)
					{
						TaskInfo* info = infoIter->second;
						info->deleted = true;
					}
				}
			}
		}

		inline Scheduler::TaskInfo* Scheduler::NewInfo(Task* task, TaskTrigger* trigger)
		{
			TaskInfo* taskInfo = new TaskInfo;
			taskInfo->task = task;
			taskInfo->trigger = trigger;
			taskInfo->deleted = false;
			return taskInfo;
		}

		inline void Scheduler::DeleteInfo(TaskInfo* info)
		{
			delete info->trigger;
			delete info;
		}

		inline Scheduler::InfoList& Scheduler::GetInfoList(TaskPriority::Enum priority)
		{
			InfoMap::iterator iter = mInfoMap.find(priority);
			if (mInfoMap.end()  != iter)
			{
				return iter->second;
			}

			InfoList infoList;
			std::pair<InfoMap::iterator, bool> p = mInfoMap.insert(std::make_pair(priority, infoList));
			return p.first->second;
		}
	}
}
